package net.sf.rose.boot;

import java.io.BufferedWriter;
import java.io.File;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.PropertyConfigurator;

import net.sf.rose.boot.bean.BootStartBean;
import net.sf.rose.boot.bean.BootStrapBean;
import net.sf.rose.boot.bean.RunTaskBean;
import net.sf.rose.boot.bean.StaticLocationBean;
import net.sf.rose.file.util.FileUtils;
import net.sf.rose.file.util.JavaUtils;
import net.sf.rose.file.util.resource.ResourceBean;
import net.sf.rose.file.util.resource.ResourceIniFile;
import net.sf.rose.jdbc.bean.DataAuthorBean;
import net.sf.rose.jdbc.bean.DataAuthorDetail;
import net.sf.rose.jdbc.dao.BeanDAO;
import net.sf.rose.jdbc.query.BeanSQL;
import net.sf.rose.jdbc.service.Service;
import net.sf.rose.task.RoseTaskBean;
import net.sf.rose.task.RoseTaskThread;
import net.sf.rose.util.DataUtils;

/**
 * 程序启动类
 * 
 * @author fengjian
 * @since 2019/03/02
 */
public class BootStrap {

	/** 操作系统类型 */
	private static int osType = 0;
	public static int OS_TYPE_LINUX = 0; // linux操作系统
	public static int OS_TYPE_WINDOWS = 1; // windows操作系统

	/** 资源文件路径 */
	private static File resourcePath = null;

	/** 用户工作路径 */
	private static File workPath = null;

	/** 程序工程文件名 */
	private static String projectName = null;

	/** 系统配置 */
	private static BootStrapBean bootBean = null;

	/** 静态文件访问路径 */
	private static Map<String, File> staticPathMap = new HashMap<>();

	/**
	 * 获取配置好的静态文件访问路径
	 * 
	 * @param key 根据静态文件夹的键名获取文件夹对象
	 * @return 文件夹对象
	 */
	public static File getStaticPath(String key) {
		return staticPathMap.get(key);
	}

	/**
	 * 获取操作系统类型
	 * 
	 * @return 0：linux操作系统；1：windows操作系统
	 */
	public static int getOSType() {
		return osType;
	}

	/**
	 * 是否是Windows操作系统
	 * 
	 * @return true：是Windows操作系统；false：不是Windows操作系统
	 */
	public static boolean isWindows() {
		return osType == OS_TYPE_WINDOWS;
	}

	/**
	 * 是否是Linux操作系统
	 * 
	 * @return true：是Linux操作系统；false：不是Linux操作系统
	 */
	public static boolean isLinux() {
		return osType == OS_TYPE_LINUX;
	}

	/**
	 * 资源文件路径
	 * 
	 * @return 资源文件路径
	 */
	public static File getResourcePath() {
		return resourcePath;
	}

	/**
	 * 用户工作路径
	 * 
	 * @return 用户工作路径
	 */
	public static File getWorkPath() {
		return workPath;
	}

	/**
	 * 获取用户工作路径下指定文件或文件夹路径
	 * 
	 * @param fileName 文件夹或文件名
	 * @return 文件夹或文件
	 */
	public static File getWorkPathFile(String fileName) {
		return getFile(workPath, fileName);
	}

	/**
	 * 获取指定文件夹下的文件夹或文件名
	 * 
	 * @param file     根路径
	 * @param fileName 文件夹或文件名
	 * @return 文件夹或文件
	 */
	public static File getFile(File file, String fileName) {
		fileName = fileName.replace('\\', '/');
		if (fileName.indexOf('/') != -1) {
			for (String s : fileName.split("/")) {
				if (!s.equals("")) {
					file = new File(file, s);
				}
			}
		} else {
			file = new File(file, fileName);
		}
		return file;
	}

	/**
	 * 获取资源文件路径下的文件或文件夹
	 * 
	 * @param fileName 文件名
	 * @return 文件
	 */
	public static File getResourceFile(String fileName) {
		// 从当前资源目录下找
		File f = new File(resourcePath, fileName);
		if (f.exists()) {
			return f;
		}
		// 从上级目录找资源目录文件夹，找到后再找文件
		boolean flag = true;
		File file = resourcePath.getParentFile();
		while (flag) {
			File path = new File(file, "resource");
			if (path.exists()) {
				f = getFile(path, fileName);
				if (f.exists()) {
					return f;
				}
			}
			file = file.getParentFile();
			if (file == null || file.getName().equals(projectName)) {
				break;
			}
		}
		return null;
	}

	/**
	 * 从项目根目录开始查找指定的配置文件
	 * 
	 * @param fileName 文件名
	 * @return 文件
	 */
	public static File getSystemFile(String fileName) {
		// 从当前资源目录下找
		File parent = resourcePath.getParentFile();
		while (parent != null) {
			File f = new File(parent, fileName);
			if (f.exists()) {
				return f;
			}
			parent = parent.getParentFile();
		}
		File f = new File(parent, fileName);
		if (f.exists()) {
			return f;
		}
		return null;
	}

	/**
	 * 获取系统工程名称
	 * 
	 * @return 启动文件所在的文件夹名称
	 */
	public static String getProjectName() {
		return projectName;
	}

	/**
	 * 获取系统配置定义类
	 * 
	 * @return 定义类对象
	 */
	public static BootStrapBean getBootBean() {
		return bootBean;
	}

	/**
	 * 读取启动配置文件，并依次执行内容 此方法适用于应用程序模式
	 * 
	 * @param args 启动参数
	 */
	public static void initial(String[] args) {
		// 简单生成启动配置数据
		simpleInit();
		// 加载Log4j配置文件
		PropertyConfigurator.configure(new File(resourcePath, "log4j.properties").getAbsolutePath());
		// 目前启动参数只有一个用途，就是是否需要保存Java源码后编绎代码
		if (args.length > 0) {
			JavaUtils.autoCompile = true;
		}
		// 初始化查找Java源文件路径以及Class文件路径
		FileUtils.initial();
		// 系统配置
		bootBean = new BootStrapBean();
		bootBean.load();
		// 静态文件访问路径
		for (StaticLocationBean b : bootBean.getWebServerBean().getLocationList()) {
			File f = null;
			String path = b.getPath();
			// 如果是带硬盘盘符的全路径
			if (path.indexOf(':') != -1) {
				f = new File(path);
			} else {
				// 从上级目录找资源目录文件夹，找到后再找文件
				boolean flag = true;
				File parent = FileUtils.JAVA_CLASS_PATH;
				while (flag) {
					f = new File(parent, path);
					if (f.exists()) {
						break;
					}
					parent = parent.getParentFile();
					if (parent == null) {
						break;
					}
				}
			}
			if (f != null && f.exists()) {
				b.setFile(f);
				String key = b.getKey();
				staticPathMap.put(key, f);
				if (key.equals("images")) {
					FileUtils.IMAGE_PATH = f;
				} else if (key.equals("webapp")) {
					FileUtils.WEB_APP_PATH = f;
				}
			}
		}
		// 读取配置文件
		File f = getResourceFile("config.ini");
		ResourceIniFile rf = new ResourceIniFile();
		rf.load(f);
		List<ResourceBean> rl = rf.getAll();
		String[] array = new String[rl.size()];
		for (int i = 0; i < rl.size(); i++) {
			array[i] = rl.get(i).getVal();
		}
		// 初始化数据库连接池
		RoseBoot.main(array);
		if (Service.isUsedDatabase()) {
			// 加载页面数据权限缓存
			Service service = new Service();
			BeanDAO dao = new BeanDAO(service);
			BeanSQL query = dao.getQuerySQL();
			// 加载所有有效的菜单数据
			query.setEntityClass(DataAuthorBean.class);
			query.createSql();
			List<DataAuthorBean> alist = dao.list();
			Map<String, DataAuthorBean> pagePower = new HashMap<>();
			for (DataAuthorBean bean : alist) {
				pagePower.put(bean.getMenuID(), bean);
			}
			// 加载所有的授权明细记录
			query.setEntityClass(DataAuthorDetail.class);
			query.createSql();
			List<DataAuthorDetail> dlist = dao.list();
			for (DataAuthorDetail bean : dlist) {
				DataAuthorBean author = pagePower.get(bean.getMenuID());
				if (author != null) {
					if (bean.getRoleID() != null) {
						author.getAuthorRoleMap().put(bean.getRoleID(), bean);
					} else if (bean.getUserID() != null) {
						author.getAuthorUserMap().put(bean.getUserID(), bean);
					}
				}
			}
			Service.setPagePowerCache(pagePower);
			// 从数据库查找所有需要开机启动类
			query.setEntityClass(BootStartBean.class);
			query.createSql("needStart", 1);
			List<BootStartBean> bslist = dao.list();
			Map<String, BootStartBean> bsMap = new HashMap<>();
			for (BootStartBean bean : bslist) {
				bsMap.put(bean.getClassName(), bean);
			}
			// 从数据库查找所有需要定时任务
			query.setEntityClass(RoseTaskBean.class);
			query.createSql("needStart", 1);
			List<RoseTaskBean> tasklist = dao.list();
			Map<String, RoseTaskBean> taskMap = new HashMap<>();
			for (RoseTaskBean bean : tasklist) {
				taskMap.put(bean.getClassName(), bean);
			}
			for (RunTaskBean task : bootBean.getRunTaskList()) {
				int type = task.getTaskType();
				// 如果是开机启动类
				if (type == 1 || type == 3) {
					BootStartBean bean = bsMap.get(task.getClassName());
					if (bean != null) {
						BootStart bs = DataUtils.getInstance(BootStrap.class, bean.getClassName());
						bs.setDescription(bean.getDescription());
						bs.setNeedStart(bean.getNeedStart());
						bs.setJson(bean.getDataJson());
						bs.start();
					}
				}
				// 如果是定时任务类
				if (type == 2 || type == 3) {
					RoseTaskBean bean = taskMap.get(task.getClassName());
					if (bean != null) {
						startTask(bean);
					}
				}
			}
			service.close();
		}
	}

	/**
	 * 简单生成启动配置数据
	 */
	public static void simpleInit() {
		System.out.println("rose core is doing...");
		String projectname = System.getProperty("user.dir");
		File file = new File(projectname);
		BootStrap.projectName = file.getName();
		// 判断操作系统类型，并据此设置配置文件路径
		String os = System.getProperty("os.name").toLowerCase();
		if (os.indexOf("linux") != -1) {
			osType = OS_TYPE_LINUX;
		} else if (os.indexOf("windows") != -1) {
			osType = OS_TYPE_WINDOWS;
		}
		// 用户工作路径
		workPath = new File(System.getProperty("user.dir"));
		// 资源文件路径
		file = new File(BootStrap.class.getResource("/").getPath());
		File f = new File(file, "resource");
		while (!f.exists() && file.getParentFile() != null) {
			file = file.getParentFile();
			f = new File(file, "resource");
		}
		resourcePath = f;
		System.out.println("系统找到用户工作路径：" + workPath);
		System.out.println("系统找到资源文件路径：" + resourcePath);
	}

	/**
	 * 启动一个任务线程
	 * 
	 * @param bean 任务定义
	 */
	public static boolean startTask(RoseTaskBean bean) {
		RoseTaskThread thread = new RoseTaskThread();
		thread.start(bean);
		return true;
	}

	/**
	 * 停止一个任务线程
	 * 
	 * @param bean 任务定义
	 */
	public static void stopTask(RoseTaskBean bean) {
		RoseTaskThread rt = RoseTaskThread.getThread(bean);
		if (rt != null) {
			rt.setContinueDo(false);
		}
	}

	/**
	 * 获取当前所有线程
	 * 
	 * @return
	 */
	public static List<RoseTaskThread> getAllThreadList() {
		return RoseTaskThread.getAllThreadList();
	}

	/**
	 * 重启项目
	 * 
	 * @return true：重启成功；false：重启失败
	 */
	public static boolean restart() {
		Socket socket = null;
		try {
			BufferedWriter out;
			socket = new Socket("localhost", BootStrap.getBootBean().getPort());
			socket.setSoTimeout(100);
			out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			// 发送指令
			out.write("restart,restart");
			out.flush();
			return true;
		} catch (Exception e) {
			return false;
		} finally {
			try {
				socket.close();
			} catch (Exception e) {
			}
		}
	}

}
